import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * Klasa sluzaca do wygenerowania populacji poczatkowej. Uzywana w algorytmie
 * genetycznym.
 * 
 * @author Ewa Byra (nr indeksu s6405)
 * @see GeneticAlgorithm
 * 
 */
public class Population {
	private static Random generator = new Random();

	/**
	 * Metoda losujaca poczatkowa populacje. Wywoluje metode
	 * getPopulationCodes() w celu wylosowania kodow binarnych nowej populacji,
	 * a nastepnie dla kazdego wylosowanego elementu obliczana jest funkcja
	 * przystosowania.
	 * 
	 * @param data
	 *            Zbior instancji problemu
	 * @param populationSize
	 *            Wielkosc populacji.
	 * @return Zwaca poczatkowa populacje.
	 */
	public static List<Chromosome> getPopulation(List<Integer> data,
			int populationSize) {
		List<Chromosome> chromosomes = new ArrayList<>();
		List<String> chromosomesCodes = getPopulationCodes(data, populationSize);

		for (int i = 0; i < populationSize; i++) {
			chromosomes.add(new Chromosome(chromosomesCodes.get(i), data));
		}

		return chromosomes;
	}

	/**
	 * Metoda sluzy do wylosowania kodow binarnych populacji poczatkowej. W celu
	 * unikniecia powtorzen generowany chromosom jest sprawdzany w metodzie
	 * validate().
	 * 
	 * @param data
	 *            Zbior instancji problemu
	 * @param populationSize
	 *            Parametr okreslajacy wielkosc populacji.
	 * @return Zwracana jest lista zakodowanych binarnie chromosomów.
	 */
	private static List<String> getPopulationCodes(List<Integer> data,
			int populationSize) {
		List<String> population = new ArrayList<>();
		String s = "";

		for (int i = 0; i < populationSize; i++) {

			while (!validate(population, s)) {
				s = "";
				for (int j = 0; j < data.size(); j++) {
					s += "" + generator.nextInt(2);
				}
			}

			population.add(s);
		}

		return population;
	}

	/**
	 * Metoda sluzy do sprawdzenia czy wylosowany chromosom nie znajduje sie juz
	 * w populacji.
	 * 
	 * @param population
	 *            Populacja osobnikow.
	 * @param chromosome
	 *            Chromosom do sprawdzenia.
	 * @return Zwraca true jezeli chromosom znajduje sie w populacji.
	 */
	private static boolean validate(List<String> population, String chromosome) {
		boolean result = true;

		String oppositeChromosome = "";

		for (char c : chromosome.toCharArray()) {
			if (c == '0') {
				oppositeChromosome += '1';
			} else {
				oppositeChromosome += '0';
			}
		}

		if (chromosome.equals("")) {
			result = false;
		} else {
			for (String s : population) {
				if (s.equals(chromosome) || s.equals(oppositeChromosome)) {
					result = false;
					break;
				}
			}
		}

		return result;
	}

}
